#include <iostream>

#include "Poco/DateTimeFormat.h"
#include "Poco/DateTimeFormatter.h"
#include "Poco/Net/HTTPRequestHandler.h"
#include "Poco/Net/HTTPRequestHandlerFactory.h"
#include "Poco/Net/HTTPServer.h"
#include "Poco/Net/HTTPServerParams.h"
#include "Poco/Net/HTTPServerRequest.h"
#include "Poco/Net/HTTPServerResponse.h"
#include "Poco/Net/ServerSocket.h"
#include "Poco/Timestamp.h"
#include "Poco/Util/Application.h"
#include "Poco/Util/HelpFormatter.h"
#include "Poco/Util/Option.h"
#include "Poco/Util/OptionSet.h"
#include "Poco/Util/ServerApplication.h"

using Poco::Net::HTTPRequestHandler;
using Poco::Net::HTTPRequestHandlerFactory;
using Poco::Net::HTTPServer;
using Poco::Net::HTTPServerParams;
using Poco::Net::HTTPServerRequest;
using Poco::Net::HTTPServerResponse;
using Poco::Net::ServerSocket;

using Poco::DateTimeFormat;
using Poco::DateTimeFormatter;
using Poco::Timestamp;

using Poco::Util::Application;
using Poco::Util::HelpFormatter;
using Poco::Util::Option;
using Poco::Util::OptionCallback;
using Poco::Util::OptionSet;
using Poco::Util::ServerApplication;

class TimeRequestHandler : public HTTPRequestHandler {
public:
  TimeRequestHandler(const std::string &format) : _format(format) {}

  void handleRequest(HTTPServerRequest &req, HTTPServerResponse &res) override {
    Application &app = Application::instance();

    app.logger().information("Request from %s", req.clientAddress().toString());

    Timestamp now;
    std::string dt(DateTimeFormatter::format(now, _format));

    res.setChunkedTransferEncoding(true);
    res.setContentType("text/html");

    std::ostream &ostr = res.send();

    ostr << "<html><head><title>HTTPTimeServer powered by "
            "POCO C++ Libraries</title>";
    ostr << "<meta http-equiv=\"refresh\" content=\"1\"></head>";
    ostr << "<body><p style=\"text-align: center; "
            "font-size: 48px;\">";
    ostr << dt;
    ostr << "</p></body></html>";
  }

private:
  std::string _format;
};

class TimeRequestHandlerFactory : public HTTPRequestHandlerFactory {
public:
  TimeRequestHandlerFactory(const std::string &format) : _format(format) {}

  HTTPRequestHandler *
  createRequestHandler(const HTTPServerRequest &req) override {
    if (req.getURI() == "/") {
      return new TimeRequestHandler(_format);
    } else {
      return nullptr;
    }
  }

private:
  std::string _format;
};

class HTTPTimeServer : public ServerApplication {
protected:
  void initialize(Application &self) override {
    loadConfiguration();
    ServerApplication::initialize(self);
  }

  void defineOptions(OptionSet &options) override {
    ServerApplication::defineOptions(options);

    options.addOption(Option("help", "h", "display argument help information")
                          .required(false)
                          .repeatable(false)
                          .callback(OptionCallback<HTTPTimeServer>(
                              this, &HTTPTimeServer::handleHelp)));
  }

  void handleHelp(const std::string &name, const std::string &value) {
    HelpFormatter helpFormatter(options());
    helpFormatter.setCommand(commandName());
    helpFormatter.setUsage("OPTIONS");
    helpFormatter.setHeader(
        "A web server that serves the current date and time.");
    helpFormatter.format(std::cout);
    stopOptionsProcessing();
    _helpRequested = true;
  }

  int main(const std::vector<std::string> &args) override {
    if (!_helpRequested) {
      unsigned short port = static_cast<unsigned short>(
          config().getInt("HTTPTimeServer.port", 9980));
      std::string format(config().getString("HTTPTimeServer.format",
                                            DateTimeFormat::HTTP_FORMAT));

      ServerSocket svs(port);
      HTTPServer srv(new TimeRequestHandlerFactory(format), svs,
                     new HTTPServerParams);
      srv.start();
      waitForTerminationRequest();
      srv.stop();
    }
    return Application::EXIT_OK;
  }

private:
  bool _helpRequested = false;
};

int main(int argc, char **argv) {
  HTTPTimeServer app;

  return app.run(argc, argv);
}
